package cn.fh.fabricsdk;

import org.hyperledger.fabric.sdk.*;
import org.hyperledger.fabric.sdk.security.CryptoSuite;
import org.hyperledger.fabric_ca.sdk.HFCAClient;

import java.io.File;
import java.nio.file.Paths;
import java.util.Collection;
import java.util.Properties;
import java.util.concurrent.TimeUnit;

import static java.lang.String.format;



public class Demo {

    static void out(String format, Object... args) {

        System.err.flush();
        System.out.flush();

        System.out.println(format(format, args));
        System.err.flush();
        System.out.flush();

    }

    private static final String EXPECTED_EVENT_NAME = "event";

    public static void main(String[] args) throws Exception {
        enroll("admin", "adminpw", "cert");
        query();
        //invoke();

    }



    /**
     * 更新账本
     * @throws Exception
     */
    private static void invoke() throws Exception {
        HFClient client = HFClient.createNewInstance();
        Channel channel = initChannel(client);

        // 构建proposal
        TransactionProposalRequest req = client.newTransactionProposalRequest();
        // 指定要调用的chaincode
        ChaincodeID cid = ChaincodeID.newBuilder().setName("mycc").build();
        req.setChaincodeID(cid);
        req.setFcn("invoke");
        req.setArgs("a", "b","10");
        System.out.println("Executing for invoke ");
        // 发送proprosal
        Collection<ProposalResponse> resps = channel.sendTransactionProposal(req);

        // 提交给orderer节点
        channel.sendTransaction(resps);
    }

    /**
     * 查询账本
     * @throws Exception
     */
    private static void query() throws Exception {
        HFClient client = HFClient.createNewInstance();
        Channel channel = initChannel(client);

        String key = "a";

        // 构建proposal
        QueryByChaincodeRequest req = client.newQueryProposalRequest();
        // 指定要调用的chaincode
        ChaincodeID cid = ChaincodeID.newBuilder().setName("mycc").build();
        req.setChaincodeID(cid);
        req.setFcn("query");
        req.setArgs(key);
        System.out.println("Querying for " + key);
        Collection<ProposalResponse> resps = channel.queryByChaincode(req);

        for (ProposalResponse resp : resps) {
            System.out.println("response: " + resp.getMessage());
            String payload = new String(resp.getChaincodeActionResponsePayload());
            System.out.println("response: " + payload);
        }
    }

    /**
     * 用户注册, 保存证书和私钥
     *
     * @param username Fabric CA Admin用户的用户名
     * @param password Fabric CA Admin用户的密码
     * @param certDir 目录名, 用来保存证书和私钥
     * @throws Exception
     */
    private static void enroll(String username, String password, String certDir) throws Exception {
        HFClient client = HFClient.createNewInstance();
        CryptoSuite cs = CryptoSuite.Factory.getCryptoSuite();
        client.setCryptoSuite(cs);

        Properties prop = new Properties();
        prop.put("verify", false);
        HFCAClient caClient = HFCAClient.createNewInstance("http://112.74.172.252:7054", prop);
        caClient.setCryptoSuite(cs);


        // enrollment保存了证书和私钥
        Enrollment enrollment = caClient.enroll(username, password);
        System.out.println(enrollment.getCert());

        // 保存到本地文件
        CertUtils.saveEnrollment(enrollment, certDir, username);
    }

    private static Channel initChannel(HFClient client) throws Exception {
        CryptoSuite cs = CryptoSuite.Factory.getCryptoSuite();
        client.setCryptoSuite(cs);

        client.setUserContext(
                new CarUser(
                        "admin",
                        CertUtils.loadEnrollment("cert", "admin")
                )
        );



        // 初始化channel
        Channel channel = client.newChannel("mychannel");
        channel.addPeer(client.newPeer("peer0.org1.example.com", "grpcs://112.74.172.252:7051",getPeerProperty("org1.example.com","peer0.org1.example.com")));
//        channel.addPeer(client.newPeer("peer1.org1.example.com", "grpcs://172.18.5.55:7051",getPeerProperty("org1.example.com","peer1.org1.example.com")));
//        channel.addPeer(client.newPeer("peer0.org2.example.com", "grpcs://172.18.5.56:7051",getPeerProperty("org2.example.com","peer0.org2.example.com")));
//        channel.addPeer(client.newPeer("peer1.org2.example.com", "grpcs://172.18.5.57:7051",getPeerProperty("org2.example.com","peer1.org2.example.com")));
        // 指定排序节点地址, 无论是后面执行查询还是更新都必须指定排序节点
        channel.addOrderer(client.newOrderer("orderer.example.com", "grpcs://112.74.172.252:7050",getOrderProperty()));
        channel.initialize();

        return channel;
    }




    private static Properties getOrderProperty(){

        File ordererCert = Paths.get(getCryptoConfigPath(), "/ordererOrganizations/example.com/orderers/orderer.example.com/tls/server.crt").toFile();

        Properties ordererProperties = new Properties();
        ordererProperties.setProperty("pemFile", ordererCert.getAbsolutePath());
        ordererProperties.setProperty("hostnameOverride", "orderer.example.com");
        ordererProperties.setProperty("sslProvider", "openSSL");
        ordererProperties.setProperty("negotiationType", "TLS");
        ordererProperties.put("grpc.ManagedChannelBuilderOption.maxInboundMessageSize", 9000000);
        // 设置keepAlive以避免在不活跃的http2连接上超时的例子。在5分钟内，需要对服务器端进行更改，以接受更快的ping速率。
        ordererProperties.put("grpc.NettyChannelBuilderOption.keepAliveTime", new Object[]{5L, TimeUnit.MINUTES});
        ordererProperties.put("grpc.NettyChannelBuilderOption.keepAliveTimeout", new Object[]{8L, TimeUnit.SECONDS});
        ordererProperties.setProperty("ordererWaitTimeMilliSecs", "300000");
        return ordererProperties;
    }


    private static Properties getPeerProperty(String orgDomainName,String peerName){

        File peerCert = Paths.get(getCryptoConfigPath(), "/peerOrganizations", orgDomainName, "peers", peerName, "tls/server.crt")
                .toFile();
        Properties peerProperties = new Properties();
        peerProperties.setProperty("pemFile", peerCert.getAbsolutePath());
        // ret.setProperty("trustServerCertificate", "true"); //testing
        // environment only NOT FOR PRODUCTION!
        peerProperties.setProperty("hostnameOverride", peerName);
        peerProperties.setProperty("sslProvider", "openSSL");
        peerProperties.setProperty("negotiationType", "TLS");
        // 在grpc的NettyChannelBuilder上设置特定选项
        peerProperties.put("grpc.ManagedChannelBuilderOption.maxInboundMessageSize", 9000000);
        return peerProperties;
    }


    /**
     * 获取crypto-config配置路径
     *
     * @return /WEB-INF/classes/fabric/crypto-config/
     */
    private static String getCryptoConfigPath() {
        String directorys = Demo.class.getClassLoader().getResource("fabric").getFile();
        System.out.println("directorys = " + directorys);
        File directory = new File(directorys);
        System.out.println("directory = " + directory.getPath());

        return directory.getPath() + "/crypto-config/";
    }


}
